<?php
namespace ZyucMini\Core;

/**
 * 数据库操作类 - 负责数据库连接和SQL执行
 * 根据配置连接数据库，执行SQL语句，返回结果，事务管理等功能。
 * 
 * @package ZyucMini\Core
 * @author 黄凯
 */
class Database
{
    /**
     * 数据库连接实例
     * @var \PDO|null
     */
    protected $connection = null;

    /**
     * 数据库配置
     * @var array
     */
    protected $config = [
        'type' => 'mysql', // mysql 或 sqlite
        'host' => 'localhost',
        'port' => '3306',
        'dbname' => '',
        'username' => '',
        'password' => '',
        'charset' => 'utf8mb4',
        'prefix' => '', // 表前缀
        'dsn' => null, // 自定义DSN字符串
        'options' => [
            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
            \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
            \PDO::ATTR_EMULATE_PREPARES => false,
            \PDO::ATTR_STRINGIFY_FETCHES => false,
            \PDO::ATTR_TIMEOUT => 30,
        ]
    ];
    
    
    /**
     * 是否在事务中
     * @var bool
     */
    protected $inTransaction = false;

    /**
     * 构造函数
     * @param array $config 数据库配置
     */
    public function __construct(array $config = [])
    {
        $this->config = array_merge($this->config, $config);
        $this->validateConfig();
    }
    
    /**
     * 验证数据库配置
     * @throws \InvalidArgumentException
     */
    protected function validateConfig()
    {
        if (empty($this->config['dbname'])) {
            throw new \InvalidArgumentException('Database name is required');
        }
        
        if ($this->config['type'] === 'mysql') {
            if (empty($this->config['host'])) {
                throw new \InvalidArgumentException('Database host is required for MySQL');
            }
        }
    }

    /**
     * 获取数据库连接
     * @return \PDO
     * @throws \PDOException
     */
    public function getConnection()
    {
        if ($this->connection === null) {
            $this->connect();
        }
        return $this->connection;
    }
    
    /**
     * 建立数据库连接
     * @throws \PDOException
     */
    protected function connect()
    {
        try {
            $dsn = $this->config['dsn'] ?? $this->buildDsn();
            $this->connection = new \PDO(
                $dsn,
                $this->config['username'] ?? null,
                $this->config['password'] ?? null,
                $this->config['options']
            );
            
            // 测试连接
            $this->connection->query('SELECT 1');
            
        } catch (\PDOException $e) {
            throw new \PDOException("Database connection failed: " . $e->getMessage(), 0, $e);
        }
    }

    /**
     * 构建DSN字符串
     * @return string
     */
    protected function buildDsn()
    {
        if ($this->config['type'] === 'sqlite') {
            return "sqlite:{$this->config['dbname']}";
        }

        // 默认为MySQL
        return "mysql:host={$this->config['host']};port={$this->config['port']};dbname={$this->config['dbname']};charset={$this->config['charset']}";
    }

    /**
     * 检查是否已连接
     * @return bool
     */
    public function isConnected()
    {
        return $this->connection !== null;
    }

    /**
     * 检查预处理语句是否成功
     * @param string $sql SQL语句
     * @return \PDOStatement
     * @throws \PDOException
     */
    protected function prepare($sql)
    {
        try {
            $stmt = $this->getConnection()->prepare($sql);
            if ($stmt === false) {
                $errorInfo = $this->getConnection()->errorInfo();
                throw new \PDOException("SQL prepare error: " . $errorInfo[2]);
            }
            return $stmt;
        } catch (\PDOException $e) {
            // 记录SQL语句用于调试（生产环境应记录到日志）
            error_log("SQL Error: " . $sql . " - " . $e->getMessage());
            throw $e;
        }
    }

    /**
     * 执行查询语句
     * @param string $sql
     * @param array $params
     * @return \PDOStatement
     */
    public function query($sql, $params = [])
    {
        $stmt = $this->prepare($sql);
        $stmt->execute($params);
        return $stmt;
    }

    /**
     * 执行更新语句
     * @param string $sql
     * @param array $params
     * @return int 受影响的行数
     */
    public function execute($sql, $params = [])
    {
        $stmt = $this->prepare($sql);
        $stmt->execute($params);
        return $stmt->rowCount();
    }

    /**
     * 获取单行记录
     * @param string $sql
     * @param array $params
     * @return array|null
     */
    public function fetch($sql, $params = [])
    {
        $stmt = $this->query($sql, $params);
        return $stmt->fetch() ?: null;
    }

    /**
     * 获取所有记录
     * @param string $sql
     * @param array $params
     * @return array
     */
    public function fetchAll($sql, $params = [])
    {
        $stmt = $this->query($sql, $params);
        return $stmt->fetchAll();
    }

    /**
     * 获取单个值
     * @param string $sql
     * @param array $params
     * @return mixed
     */
    public function fetchColumn($sql, $params = [], $column = 0)
    {
        $stmt = $this->query($sql, $params);
        return $stmt->fetchColumn($column);
    }

    /**
     * 开始事务
     * @return bool
     * @throws \PDOException
     */
    public function beginTransaction()
    {
        if ($this->inTransaction) {
            throw new \PDOException("Transaction already started");
        }
        
        $result = $this->getConnection()->beginTransaction();
        if ($result) {
            $this->inTransaction = true;
        }
        return $result;
    }

    /**
     * 提交事务
     * @return bool
     * @throws \PDOException
     */
    public function commit()
    {
        if (!$this->inTransaction) {
            throw new \PDOException("No active transaction to commit");
        }
        
        $result = $this->getConnection()->commit();
        if ($result) {
            $this->inTransaction = false;
        }
        return $result;
    }

    /**
     * 回滚事务
     * @return bool
     * @throws \PDOException
     */
    public function rollBack()
    {
        if (!$this->inTransaction) {
            throw new \PDOException("No active transaction to rollback");
        }
        
        $result = $this->getConnection()->rollBack();
        if ($result) {
            $this->inTransaction = false;
        }
        return $result;
    }
    
    /**
     * 检查是否在事务中
     * @return bool
     */
    public function inTransaction()
    {
        return $this->inTransaction;
    }

    /**
     * 关闭连接
     */
    public function close()
    {
        if ($this->inTransaction) {
            $this->rollBack();
        }
        $this->connection = null;
    }

    /**
     * 获取最后插入的ID
     * @return int
     */
    public function getLastId()
    {
        return (int) $this->getConnection()->lastInsertId();
    }

    /**
     * 获取表前缀
     * @return string
     */
    public function getPrefix()
    {
        return $this->config['prefix'] ?? '';
    }
    
    /**
     * 获取数据库配置
     * @return array
     */
    public function getConfig()
    {
        $config = $this->config;
        // 隐藏敏感信息
        unset($config['password']);
        return $config;
    }
    
    /**
     * 执行事务
     * @param callable $callback 事务回调函数
     * @return mixed
     * @throws \Exception
     */
    public function transaction($callback)
    {
        $this->beginTransaction();
        
        try {
            $result = $callback($this);
            $this->commit();
            return $result;
        } catch (\Exception $e) {
            $this->rollBack();
            throw $e;
        }
    }
    
    /**
     * 检查表是否存在
     * @param string $tableName 表名
     * @return bool
     */
    public function tableExists($tableName)
    {
        $sql = "SHOW TABLES LIKE ?";
        $stmt = $this->query($sql, [$tableName]);
        return $stmt->rowCount() > 0;
    }
    
    /**
     * 获取表结构
     * @param string $tableName 表名
     * @return array
     */
    public function getTableStructure($tableName)
    {
        $sql = "DESCRIBE `{$tableName}`";
        return $this->fetchAll($sql);
    }
}
